VISUALIZATION

PLOT AGP

PLOT RANGES

PLOT DAILY

MODEL RESULTS

party-model{width=“75%”, height=“75%”}

DEFINITIONS

Name iglu R function Reference Time Dependent
Active Percent Yes
ADRR (Average Daily Risk Range) Yes
AUC (Area Under the Curve) Yes
COGI (COntinuous Glucose monitoring Index) No
CONGA (Continuous Overall Net Glycemic Action) Yes
Coefficient of variation (CV) No
CVmean (mean of daily CV) Yes
CVsd (SD of daily CV) Yes
eA1c (Estimated A1c) No
Episode Calculation
Yes
Episode Calculation Profile
Yes
Glucose Management Indicator (GMI) No
GRADE (Glycemic Risk Assessment Diabetes Equation) No
GRADEeu No
GRADEhyper No
GRADEhypo No
GVP (Glucose Variability Percentage) Yes
HBGI (High Blood Glucose Index) No
LBGI (Low Blood Glucose Index) No
Hyperglycemia Index Parameters No
Hypoglycemia Index Parameters No
IGC (Index of Glycemic Control) No
IQR (Interquartile Range)
No
J-Index No
M-value No
MAD (Mean Absolute Deviation)
No
MAG (Mean Absolute Glucose) Yes
MAGE (Mean Amplitude of Glycemic Excursions) No
Mean
No
Median
No
MODD (Mean Of Daily Differences) Yes
Percent Above
No
Percent Below
No
Percent in range
No
Quantiles
No
Range
No
Rate of change (ROC) Yes
Standard Deviation of ROC Yes
Standard Deviation
No
SdB (between days, within timepoints) Yes
SdBDM (between days, within timepoints, corrected for changes in daily means) Yes
SdDM (horizontal sd) Yes
SdHHMM (between time points) Yes
SdW (vertical within days) Yes
SdWSH (within time series) Yes

MODEL RESULTS

---
title:      Continuous Glucose Monitoring (CGM) Visualization
author:     Agustin Calatroni <a href='https://github.com/agstn/CGM'> <i style='background-color:white' class='fa fa-github'> Wonderful-Wednesdays </i></a>
date:       "`r format(Sys.Date(), format='%a %d %b %Y')`" 
output: 
  flexdashboard::flex_dashboard:
    self_contained: true
    source_code: embed
---

```{=html}
<style>

element.style {
width: 900px;
height: 550px;
margin-top: 10px;
margin-bottom: 10px;
}

</style>
```

```{r knitr-defaults}
knitr::opts_chunk$set(warning = FALSE, message = FALSE, comment = NA)
knitr::opts_chunk$set(cache = FALSE)
options(width=170)
```

```{r load-packages}
pacman::p_load(tidyverse, rio, gt)
pacman::p_load(labelled)
pacman::p_load(patchwork)
pacman::p_load(iglu)
pacman::p_load(partykit, ggparty)
pacman::p_load(ggdist)
```

```{r import-reshape-label}
CGM_data_1 <- import('https://raw.githubusercontent.com/VIS-SIG/Wonderful-Wednesdays/master/data/2021/2021-08-11/simulated_data.csv')

CGM_data_2 <- CGM_data_1 %>% 
   rename(VISITNUM_N = VISITNUM) %>% 
   mutate(VISITNUM_C = factor(VISITNUM_N, labels = c('Baseline','26-weeks','52-weeks')),
          VISITNUM_M = case_when(VISITNUM_N == 3  ~ '01',
                                 VISITNUM_N == 17 ~ '06',
                                 VISITNUM_N == 21 ~ '12'),
          TREATMENT = factor(TREATMENT ) %>% 
             fct_relevel("SOC","Rx low","Rx medium","Rx high") %>% 
             fct_recode(`Rx-Low` = "Rx low",
                        `Rx-Med` = "Rx medium",
                        `Rx-High` = "Rx high")) %>% 
   rename(VALUE_ORIG = 'Original_CGMValue',
          VALUE_SIM  = 'Simulated_CGMValue',
          TIME = 'CGMTIME',
          DAY = 'CGMDAY') %>% 
   select(SUBJID,
          TREATMENT,
          VISITNUM_N,
          VISITNUM_C,
          VISITNUM_M,
          DAY,
          TIME,
          VALUE_ORIG,
          VALUE_SIM) %>% 
   mutate(DAY_TIME = as.POSIXct(str_glue("2021-{VISITNUM_M}-0{DAY} {TIME}:00")),
          .after = TIME) 

var_label(CGM_data_2) <- list(
   SUBJID     = 'Subject Identification',
   TREATMENT  = 'Treatment Regimen',
   VISITNUM_N = 'Study visit NUM',
   VISITNUM_M = 'Study visit MONTH',
   VISITNUM_C = 'Study Visit NAME',
   DAY        = 'Day the CGM was measured',
   TIME       = 'Time of the day that the CGM value was measured',
   DAY_TIME   = 'Day/Time created',
   VALUE_ORIG =  'Original CGM value',
   VALUE_SIM  =  'Simulated CGM value')

export(CGM_data_2, "CGM_data.rds")
```

```{r iglue-function}
plot_ranges_2 <- function (data) {
   
   gl = id = below_54 = below_70 = in_range_70_180 = above_180 = above_250 = percent = NULL
   
   rm(list = c("gl", "id", "below_54", "below_70", "in_range_70_180", "above_180", "above_250", "percent"))
   subject = unique(data$id)
   ns = length(subject)
   if (ns > 1) {
      subject = subject[1]
      warning(paste("The provided data have", ns, "subjects. The plot will only be created for subject", 
                    subject))
      data = data %>% dplyr::filter(id == subject)
   }
   
   ranges <- agp_metrics(data, shinyformat = FALSE) %>% 
      dplyr::select(-c("id", "active_percent", "mean", "GMI", "CV")) %>% 
      dplyr::summarise(range = c("very_low", "low", "target", "high", "very_high"), 
                       percent = c(below_54, below_70 - below_54, in_range_70_180, above_180 - above_250, above_250))
   
   ranges = ranges %>% 
      dplyr::mutate(range = factor(range, levels = c("very_high",  "high", "target", "low", "very_low")))
   
   colors <- c("#F9B500", "#F9F000", "#48BA3C", "#F92D00", "#8E1B1B")
   
   ggplot(data = ranges, mapping = aes(y = '', fill = range, x = percent)) + 
      geom_bar(stat = "identity") + 
      scale_fill_manual(values = colors, 
                        drop = FALSE,
                        labels = c("Very High\n>250", 
                                   "High\n181-250", 
                                   "Target\n70-180", 
                                   "Low\n54-69", 
                                   "Very Low\n<54")) + 
      scale_x_continuous(breaks = seq(0, 100, 25)) + 
      labs(x = NULL,
           y = NULL,
           fill = "Percent (%) Time spent in \nstandardized glycemic ranges") + 
      scale_y_discrete(breaks = NULL) +
      guides(fill = guide_legend(reverse = TRUE,
                                 label.position = 'bottom')) 
}
```

```{r iglu-derive}
options(warn=-1)

CGM_iglu_data <- CGM_data_2 %>% 
   mutate(studyid = SUBJID) %>% 
   process_data(id = "SUBJID", timestamp = "DAY_TIME", glu = "VALUE_ORIG") %>% 
   select(-visitnum_n, -visitnum_m, -time.1, -value_sim, -day) %>% 
   rename(visit = visitnum_c) %>% 
   nest_by(studyid, treatment, visit)

CGM_iglu_data %>% 
   ungroup() %>% 
   count(studyid) %>% 
   filter(n == 3) %>% 
   pull(studyid) -> CGM_n3

export(CGM_n3, "CGM_n3.rds")

CGM_iglu_metrics <- CGM_iglu_data %>%
   rowwise() %>% 
   mutate(all_metrics  = list( data %>% data.frame() %>% all_metrics() )) %>% 
   mutate(panel     = list( 
      (plot_ranges_2(data)) / 
         (guide_area()) / 
         (plot_agp(data)   + ylim(39, 401)) / 
         (plot_daily(data) + ylim(39, 401) +
             geom_ribbon(aes(reltime, ymin = 70, ymax = 180), fill = "#48BA3C", alpha = 0.3) +
             geom_hline(yintercept = c(70, 180), color = "#48BA3C")) /
         plot_layout(heights = c(1, 1, 3, 3),
                     guides = 'collect') &
         theme_bw() &
         theme(legend.position='bottom',
               legend.key.height = unit(0.3, 'cm'),
               legend.key.width  = unit(1.5, 'cm'),
               strip.background = element_blank(), 
               strip.text = element_blank(),
               legend.title=element_text(size=9))
      
   ) 
   ) %>%
   unnest(c(all_metrics))

export(CGM_iglu_metrics, "CGM_iglu_metrics.rds")
```

# VISUALIZATION {data-navmenu="AGP"}

```{r iglu-trelliscope}
pacman::p_load(trelliscopejs)

CGM_iglu_metrics %>% 
   select(studyid, treatment, visit,
          ADRR, AUC, COGI, Conga, eA1C, GMI, GVP, HBGI, LBGI, IGC,
          panel) %>% 
   ungroup() %>% 
   set_labels(list(ADRR = 'average daily risk range', 
                   AUC = 'Area Under Curve', 
                   COGI = 'Continuous GlucoseMonitoring Index', 
                   Conga = 'Continuous Overall Net Glycemic Action', 
                   eA1C = 'Estimated A1c', 
                   GMI = 'Glucose Management Indicator', 
                   GVP = 'Glucose Variability Percentage', 
                   HBGI = 'High Blood Glucose Index', 
                   LBGI = 'Low Blood Glucose Index', 
                   IGC = 'Index of Glycemic Control')) %>%
   mutate(across(where(is.numeric), ~round(.x, 2))) %>% 
   trelliscope(name = 'CGM: Continuous Glucose Monitoring Visualization',
               desc = 'Display Ambulatory Glucose Profile (AGP) statistics (for subjects with ALL 3 visits)',
               panel_col = 'panel',
               path = './CGM-trelliscope',
               ncol = 3,
               nrow = 1,
               height = 900,
               width  = 700,
               state = list(sort = list(sort_spec('studyid')))
   )
```

# PLOT AGP {data-navmenu="AGP"}
```{r, out.width="1000px", out.extra="data-external=1"}
pacman::p_load(webshot)
knitr::include_url("https://irinagain.github.io/iglu/reference/plot_agp.html", height = "800px")
```

# PLOT RANGES {data-navmenu="AGP"}
```{r, out.width="1000px", out.extra="data-external=1"}
knitr::include_url("https://irinagain.github.io/iglu/reference/plot_ranges.html", height = "800px")
```

# PLOT DAILY {data-navmenu="AGP"}
```{r, out.width="1000px", out.extra="data-external=1"}
knitr::include_url("https://irinagain.github.io/iglu/reference/plot_daily.html", height = "800px")
```

# MODEL RESULTS

```{r party-model}
CGM_data_m1 <- CGM_iglu_metrics %>%
   select(studyid, treatment, visit, data) %>%
   rowwise() %>% 
   mutate(below_percent(data, targets_below = 53.9)[2], 
          below_percent(data, targets_below = 69.9)[2], 
          in_range_percent(data, target_ranges = list(c(70, 180)))[2], 
          above_percent(data, targets_above = 180.1)[2], 
          above_percent(data, targets_above = 250.1)[2]) %>% 
   mutate(below_69.9 = below_69.9 - below_53.9, 
          above_180.1 = above_180.1 - above_250.1) %>%
   select(-data) %>% 
   pivot_longer(cols = -c(1:3),
                names_to = 'state',
                values_to = 'pct') %>% 
   mutate(state = as.factor(state) %>% 
             fct_relevel('below_53.9',
                         'below_69.9',
                         'in_range_70_180',
                         'above_180.1',
                         'above_250.1') %>% 
             fct_rev()) 

target_plot <- CGM_data_m1 %>% 
   lmtree(pct ~ state | treatment + visit, data = .,
          alpha = 0.10, bonferroni  = FALSE) %>% 
   ggparty(terminal_space = 0.6, horizontal = TRUE) +
   geom_edge() +
   geom_edge_label() +
   geom_node_label(line_list = list(aes(label = splitvar),
                                    aes(label = gtsummary::style_pvalue(p.value, digits = 2, prepend_p = TRUE))),
                   line_gpar = list(list(size = 9),
                                    list(size = 9, fontface = 'bold')),
                   ids = "inner") +
   geom_node_plot(
      gglist = list(
         geom_bar(aes(x = pct, y = "", fill = state),
                  position = 'stack', stat = "summary", fun = "mean"),
         scale_y_discrete(breaks = NULL),
         scale_fill_manual(values = c("#F9B500", "#F9F000", "#48BA3C", "#F92D00", "#8E1B1B"), 
                           drop = FALSE, 
                           labels = c("Very High\n>250", 
                                      "High\n181-250", 
                                      "Target\n70-180", 
                                      "Low\n54-69", 
                                      "Very Low\n<54"),
                           guide = guide_legend(reverse = TRUE,
                                                label.position = 'bottom')),
         labs(y = NULL,
              x = "Percent (%)",
              fill = "Percent (%) Time spent in \nstandardized glycemic ranges"),
         theme_bw(),
         theme(legend.position='bottom',
               legend.key.height = unit(0.3, 'cm'),
               legend.key.width  = unit(1.5, 'cm'),
               strip.background = element_blank(), 
               strip.text = element_blank(),
               legend.title=element_text(size=9))),
      ids = "terminal",
      shared_axis_labels = TRUE
   )

ggsave(str_glue("CGM-models/CGM_target.png"),
       plot = target_plot,
       scale = 4,
       width = 900, height = 600, units = 'px')
```

![party-model](CGM-models/CGM_target.png){width="75%", height="75%"}

# DEFINITIONS {data-navmenu="OTHER METRICS"}

```{r}
CGM_metric <- import("CGM_metric.csv")

CGM_metric %>% 
   gt() %>% 
   cols_move_to_end(Metric_Time_dependent) %>% 
   cols_label(Metric_Name = "Name",
              Metric_Iglu = "iglu R function",
              Metric_Time_dependent = "Time Dependent") %>% 
   fmt_missing(Reference, missing_text = "-") %>% 
   fmt_markdown(c(Reference, Metric_Iglu)) %>% 
   cols_align('left') %>% 
   cols_width(Metric_Name ~ px(450),
              Metric_Iglu ~ px(250),
              Reference ~ px(250)) %>% 
   tab_options(table.font.size = "medium", 
               data_row.padding = px(2), 
               footnotes.padding = px(2), 
               source_notes.padding = px(2))
```

# MODEL RESULTS {data-navmenu="OTHER METRICS"}

```{r party-mod-viz}
# trick: https://www.johannesbgruber.eu/post/2021-07-28-let-users-choose-which-plot-you-want-to-show/

for(i in c("ADRR","AUC", "COGI", "Conga", "eA1C", "GMI", "GVP", "HBGI", "LBGI", "IGC")){
   
   metric_run <- sym(paste(i))
   
   metric_lab <- CGM_metric %>% 
      filter(str_detect(Metric_Name, paste(metric_run))) %>% 
      pull(Metric_Name)
   
   metric_plot <- CGM_iglu_metrics %>% 
      lmtree(formula(str_glue("{metric_run} ~ 1 | treatment +  visit")), 
             data = .,
             alpha = 0.10, bonferroni  = FALSE) %>% 
      ggparty(terminal_space = 0.4, horizontal = TRUE) +
      geom_edge() +
      geom_edge_label() +
      geom_node_label(line_list = list(aes(label = splitvar),
                                       aes(label = gtsummary::style_pvalue(p.value, digits = 2, prepend_p = TRUE))),
                      line_gpar = list(list(size = 9),
                                       list(size = 9, fontface = 'bold')),
                      ids = "inner") +
      geom_node_plot(
         gglist = list(
            stat_halfeye(aes(x = !!metric_run, y = ""), 
                         point_interval = mean_qi,
                         justification = -0.05),
            geom_rug(aes(x = !!metric_run), alpha = 0.25, sides = 't', length = unit(0.05, "npc")),
            geom_boxplot(aes(x = !!metric_run, y = ""), 
                         width = 0.30, outlier.shape = NA,
                         position = position_nudge(y = -0.25)),
            scale_y_discrete(breaks = NULL),
            labs(y = NULL,
                 x = metric_lab),
            theme_bw()),
         ids = "terminal",
         shared_axis_labels = TRUE) 
   
   ggsave(str_glue("CGM-models/{metric_run}.png"),
          plot = metric_plot,
          scale = 4,
          width = 850, height = 600, units = 'px')
   
}
```

<select id="imgList"> 
<option value="CGM-models/ADRR.png">ADRR (average daily risk range)</option>
<option value="CGM-models/AUC.png">AUC (Area Under Curve)</option>
<option value="CGM-models/COGI.png">COGI (Continuous GlucoseMonitoring Index)</option> 
<option value="CGM-models/Conga.png">CONGA (Continuous Overall Net Glycemic Action)</option>
<option value="CGM-models/eA1C.png">eA1c (Estimated A1c)</option>
<option value="CGM-models/GMI.png">GMI (Glucose Management Indicator)</option> 
<option value="CGM-models/GVP.png">GVP (Glucose Variability Percentage)</option>
<option value="CGM-models/HBGI.png">HBGI (High Blood Glucose Index)</option> 
<option value="CGM-models/LBGI.png">LBGI (Low Blood Glucose Index)</option> 
<option value="CGM-models/IGC.png">IGC (Index of Glycemic Control)</option>
</select>
<img src="CGM-models/ADRR.png" id="image" width="75%" height="75%"/>


<script type="text/javascript">             
var img = document.getElementById("image");

function setClass(e) {
  var select = e.target;
  img.src = select.options[select.selectedIndex].value;
  return false;
}

document.getElementById("imgList").onchange = setClass;
</script>